/* This file has methods to build a cfg */
#include "shared.h"
#include "cfg.h"
#include "y.tab.h"
#include "symtab.h"
#include "common.h"
#include "nvg.c"
#include "gre.c"

void print_tabs_cfg() 
{
	int i;
	for (i = 0; i < tabCount; i++) {
		printf("\t");
	}
}

void print_three_address_final(int line, struct Three_AddressNode *node)
{
	printf("%d ", line);
	print_tabs_cfg();
	if (node->type == THREE_ADDRESS_ASSIGN) 
	{
		if (node->parent->parents != NULL) {
			if (node->parent->parents->parent->exit->type == THREE_ADDRESS_IF && node->lineNumber == node->parent->parents->parent->exit->ptr2->entry->lineNumber)
			{
				printf("else\n");
				print_tabs_cfg();
				printf("{\n");
				tabCount++;
				print_tabs_cfg();
			}
		}
		if (node->operand1 && node->operand2) 
		{
			printf("%s = %s %s %s;\n", cfg_symtab_get_val(node->LHS), cfg_symtab_get_val(node->operand1), convert_operator(node->operator), cfg_symtab_get_val(node->operand2));
		} 
		else if (node->operand1 && !(node->operand2)) 
		{
			printf("%s = %s;\n", cfg_symtab_get_val(node->LHS), cfg_symtab_get_val(node->operand1));
		} 
		else if (!(node->operand1)&& node->operand2) 
		{
			printf("%s = %s %d;\n", cfg_symtab_get_val(node->LHS), convert_operator(node->operator), node->operand2);
		}
		if (node->next == NULL)
		{
			if (node->ptr1 != NULL && node->ptr1->exit->type == THREE_ADDRESS_WHILE) return;
			tabCount--;
			print_tabs_cfg();
			printf("}\n");
		}
	} 
	else if (node->type == THREE_ADDRESS_IF) 
	{
		printf("if (%s)\n", cfg_symtab_get_val(node->LHS));
		print_tabs_cfg();
		printf("{\n");
		tabCount++;
	} 
	else if (node->type == THREE_ADDRESS_WHILE) 
	{
		printf("while (%s)\n", cfg_symtab_get_val(node->LHS));
	       	print_tabs_cfg();
		printf("{\n");	
		tabCount++;
	} 
	else 
	{
//		printf("Dummy;\n");
	}

}

void print_all_nodes() {
	int line = 1;
	tabCount = 1;
	struct nodeList *temp = nodeList;
	while (temp != NULL) {
		if (temp->node->type != THREE_ADDRESS_DUMMY)
	            {
		    print_three_address_final(line, temp->node);
		    line++;
		    }
		temp = temp->next;
	}
	while (tabCount > 1) {
		tabCount--;
		print_tabs_cfg();
		printf("}\n");
	}
}

void remove_from_node_list (struct Three_AddressNode *node)
{
	struct nodeList *temp = nodeList;
	if (temp == NULL) return;
	if (temp->node->lineNumber == node->lineNumber) {
		nodeList = temp->next;
	} else {
		while (temp->next != NULL) {
			if (temp->next->node->lineNumber == node->lineNumber) {
				temp->next = temp->next->next;
				return;
			}
			temp = temp->next;
		}
	}
}

void add_to_node_list (struct Three_AddressNode *node)
{
	struct nodeList *newNodeList = (struct nodeList *)(malloc(sizeof(struct nodeList)));
	newNodeList->node = node;
	newNodeList->next = NULL;
	if (nodeList == NULL) {
		nodeList = newNodeList;
	} else {
		struct nodeList *temp = nodeList;
		while (temp->next != NULL) {
			temp = temp->next;
		}
		temp->next = newNodeList;
	}
}

void add_to_bb_list(struct BB *bb)
{
	struct BBList *newBBList = (struct BBList *)(malloc(sizeof(struct BBList)));
	newBBList->bb = bb;
	newBBList->next = NULL;
	if (bbList == NULL) {
		bbList = newBBList;
	} else {
		struct BBList *temp = bbList;
		while (temp->next != NULL) {
			temp = temp->next;
		}
		temp->next = newBBList;
	}
}

void reinitialize_mark (struct BB *block)
{
	struct BBList *temp = bbList;
	while (temp != NULL) {
		if (temp->bb != NULL) {
			temp->bb->marked = 0;
		}
		temp = temp->next;
	}
}


struct replaceTable *lookupReplacement(struct BB *bb, int del)
{
	struct replaceTable *temp = bb->replace;
	while (temp != NULL) 
	{
		if (temp->del == del) {
			return temp;
		}
		temp = temp->next;
	}
	return NULL;
}

void add_to_replace (struct BB *bb, int del, int rel) 
{
	// Create the new replacement rule
	struct replaceTable *newReplace = (struct replaceTable *)(malloc(sizeof(struct replaceTable)));
	newReplace->del = del;
	newReplace->replace = rel;
	newReplace->next = NULL;

	// Add rule to nvg
	if (bb->replace == NULL) {
		bb->replace = newReplace;
	} else {
		struct replaceTable *bbRep = bb->replace;
		while (bbRep->next != NULL) {
			bbRep = bbRep->next;
		}
		bbRep->next = newReplace;
	}
}

void remove_replacement (struct BB *bb, int del)
{
	struct replaceTable *bbRep = bb->replace;
	struct replaceTable *toDel = NULL;
	while (bbRep != NULL && toDel == NULL) {
		if (bbRep->next != NULL && bbRep->next->del == del) {
			bbRep->next = bbRep->next->next;
			toDel = bbRep->next;
		}
		else if (bbRep->del == del) {
			bb->replace = bbRep->next;
			toDel = bbRep;
		}
		bbRep = bbRep->next;
	}
	if (toDel != NULL) {
		free(toDel);
	}
	// The temporary variable is no longer going to be used, so delete it from the symbol table
	cfg_symtab_remove_key(del);
}

int getLength(char * str)
{
	if (str)
		return strlen(str);
	else
		return 0;
}


void add_vdl_to_cfg_symtab (struct variable_declaration_list_t *vdl)
{
	struct variable_declaration_list_t *temp = vdl;
	while (temp!=NULL)
	{
		struct identifier_list_t *tempidl = temp->vd->il;
		while (tempidl!=NULL)
		{
			int key = makekey(tempidl->id, MAX_VARIABLES);
			cfg_symtab_add(SYM_TYPE_USER, key, tempidl->id);
			tempidl = tempidl->next;
		}
		temp = temp->next;
	}
}

void build_cfg_symtab(struct class_list_t * cl)
{
	add_vdl_to_cfg_symtab (cl->cb->vdl);
	add_vdl_to_cfg_symtab(cl->cb->fdl->fd->fb->vdl);
}

void replace_free (struct replaceTable *n)
{
	struct replaceTable * temp = n;
	struct replaceTable * temp2 = n;
	while (temp!=NULL)
	{
                temp2 = temp;
                temp = temp->next;
		free (temp2);
                temp2 = NULL;
	}
}
void parents_free (struct parentBB *n)
{
	struct parentBB * temp = n;
	struct parentBB * temp2 = n;
	while (temp!=NULL)
	{
                temp2 = temp;
                temp = temp->nextparent;
		free (temp2);
                temp2 = NULL;
	}
}


void nvg_free (struct NVG * n)
{
	struct NVG * temp = n;
	struct NVG * temp2 = n;
	while (temp!=NULL)
	{
                temp2 = temp;
                temp = temp->next;
		free (temp2);
                temp2 = NULL;
	}
}

void reInit_cfg()
{
	lineNumber =1;
	exprRHSCount = 0;
	memset (exprRHS, 0, MAX_EXPR_DEPTH * sizeof(int));
	blockCount = 0;
	c = 0;
	varCount = 1;
	printCount = 1;
        currentBlock = NULL;
        currentNode = NULL;
        globalReplace = NULL;
        tempReplace = NULL;
	//cfg_symtab_reInit ();
}

void build2_cfg(struct program_t *p)
{
	cfg2 = malloc(sizeof(struct CFG));
	struct class_list_t *temp_cl;
	gP = p;
	temp_cl = p->cl;
	while (temp_cl != NULL) 
	{
		build_cfg_symtab(temp_cl);
//		printf("BUILDING CFG FOR CLASS -> %s\n", temp_cl->ci->id); 
		add_block();
		cfg2->entry = currentBlock;
		add_ThreeAddress(THREE_ADDRESS_DUMMY, -1, -1, -1, -1, NULL, -1);
		class_cfg(temp_cl); 
		currentBlock->exit = currentNode;
		cfg2->exit = currentBlock;
		temp_cl = temp_cl->next;
	}
}

void build_cfg(struct program_t *p)
{
	cfg = malloc(sizeof(struct CFG));
	struct class_list_t *temp_cl;
	gP = p;
	temp_cl = p->cl;
	while (temp_cl != NULL) 
	{
		build_cfg_symtab(temp_cl);
//		printf("BUILDING CFG FOR CLASS -> %s\n", temp_cl->ci->id);
		add_block();
		cfg->entry = currentBlock;
		add_ThreeAddress(THREE_ADDRESS_DUMMY, -1, -1, -1, -1, NULL, -1);
		class_cfg(temp_cl); 
		currentBlock->exit = currentNode;
		cfg->exit = currentBlock;
		temp_cl = temp_cl->next;
	}
}

void class_cfg(struct class_list_t *cl) 
{
	// access function block
	// access statements
	struct func_declaration_list_t * fdl = cl->cb->fdl;
	struct statement_sequence_t *ss;

	while (fdl!=NULL && fdl->fd !=NULL)
	{
		ss = fdl->fd->fb->ss;
		statement_sequence_cfg(ss);
		fdl=fdl->next;
	}

}

void statement_sequence_cfg(struct statement_sequence_t *ss) 
{
	while (ss != NULL && ss->s != NULL)
	{
		statement_cfg(ss->s);
		ss = ss ->next;
	}
}

void statement_cfg(struct statement_t *stmt) 
{
	// Find out what type of statement it is and do the appropriate action
	if (stmt->type == STATEMENT_T_ASSIGNMENT) {
		assign_statement_cfg(stmt->data.as);
	} else if (stmt->type == STATEMENT_T_IF) {
		if_statement_cfg(stmt->data.is);
	} else if (stmt->type == STATEMENT_T_WHILE) {
		while_statement_cfg(stmt->data.ws);
	} else if (stmt->type == STATEMENT_T_SEQUENCE) {
		statement_sequence_cfg(stmt->data.ss);
	}

	//printf("type of statement is %d \n", stmt->type);
}	

void if_statement_cfg(struct if_statement_t *ifStmt) 
{
	// Add code for the if statement condition 
	struct Three_AddressNode *ifNode = add_ThreeAddress(THREE_ADDRESS_IF, evaluate_expression(ifStmt->e), -1, -1, -1, NULL, -1);

	// Add a new basic block for the if statement body, and point to it from the if statement node
	add_block();
	ifNode->ptr1 = currentBlock;
	add_parent(currentBlock, ifNode->parent);

	// Create the CFG for the if statement body 
	statement_cfg(ifStmt->s1);

	// Capture the last node to link to the dummy block
	struct Three_AddressNode *endIfNode = currentNode;

	// Create the dummy block that the if and else bodies will point to
	add_block();
	struct BB *dummyBlock = currentBlock;
	endIfNode->ptr1 = dummyBlock;
	add_parent(dummyBlock, endIfNode->parent);

	struct Three_AddressNode *dummyNode = add_ThreeAddress(THREE_ADDRESS_DUMMY, -1, -1, -1, -1, NULL, -1);

	// Create a new block for the else statement body, and point to it from the original ifNode  
	add_block();
	ifNode->ptr2 = currentBlock;
	add_parent(currentBlock, ifNode->parent);

	// If the else statement has a body, create its CFG, otherwise it will be a dummy block 
	if (ifStmt->s2) {
		statement_cfg(ifStmt->s2);
	} else {
		add_ThreeAddress(THREE_ADDRESS_DUMMY, -1, -1, -1, -1, NULL, -1);
	}

	// Point the else basic block to the dummy block that if points to 
	currentNode->ptr1 = dummyBlock;
	currentBlock->exit = currentNode;
	add_parent(dummyBlock, currentNode->parent);
	currentNode = dummyNode;
	currentBlock = dummyNode->parent;

	// The dummy block had its exit set when the else statement body was created - set it back to null
	currentBlock->exit = NULL;
}	

void while_statement_cfg(struct while_statement_t *whileStmt) 
{
	// Get the current node so that it can be linked to the new block of the while statement
	struct Three_AddressNode *current = currentNode;

	// Add a new block for the while statement condition
	add_block();
	current->ptr1 = currentBlock;
	add_parent(currentBlock, current->parent);

	// Set the condition block to whatever the current block is
	struct BB *whileConditionBlock = currentBlock;
	whileConditionBlock->type = BB_TYPE_WHILE_COND;

	// Create a node for the while statement
	struct Three_AddressNode *whileNode = add_ThreeAddress(THREE_ADDRESS_WHILE, evaluate_expression(whileStmt->e), -1, -1, -1, NULL, -1);

	// Create a new basic block for the while statement body, and link to it with the while statement node 
	add_block();
	whileNode->ptr1 = currentBlock;
	currentBlock->type = BB_TYPE_WHILE_BODY;
	add_parent(currentBlock, whileNode->parent);

	// Create the CFG for the while statement body
	statement_cfg(whileStmt->s);

	// Link the end of the basic block for the while statement body with the entry point 
	struct Three_AddressNode *endWhileNode = currentNode;
	endWhileNode->ptr1 = whileConditionBlock;

	// Create a new block for the next set of statements to go into, and link with the final while block 
	add_block();
	endWhileNode->ptr2 = currentBlock;
	add_parent(currentBlock, endWhileNode->parent);
	current->ptr2 = currentBlock;
	add_parent(currentBlock, current->parent);
	add_ThreeAddress(THREE_ADDRESS_DUMMY, -1, -1, -1, -1, NULL, -1);
}	

void assign_statement_cfg(struct assignment_statement_t *assignStmt) 
{
	int LHS = makekey(assignStmt->va->data.id, MAX_VARIABLES);

	int operand1 = evaluate_expression(assignStmt->e);
	add_key_to_global_rhs(operand1);

	add_ThreeAddress(THREE_ADDRESS_ASSIGN, LHS, operand1, -1, -1, assignStmt->e->expr->type, assignStmt->e->expr->val);

	// Reset the global count of rhs - the expression is done
	exprRHSCount = 0;
}

void add_to_cfg (struct Three_AddressNode *newNode)
{
	if (currentNode == NULL) 
	{
		// No current node means there has been no entry in the currentBlock 
		currentBlock->entry = newNode;
	} else {
		// Connect the new node with the current node in the currentBlock 
		currentNode->next = newNode;
	}
	newNode->parent = currentBlock;
	currentNode = newNode;
}

void add_parent (struct BB *child, struct BB *parent) {
	// Create a new parent, and set it's parent to the parent parameter
	struct parentBB *newParent = malloc(sizeof(struct parentBB));
	newParent->parent = parent;
	struct parentBB *parents = child->parents;
	if (parents == NULL) {
		// The child has no parents, so make the new parent its only parent
		child->parents = newParent;
	} else {
		// The child has at least a parent, so traverse the list and add the parent to the list
		while (parents->nextparent != NULL) {
			parents = parents->nextparent;
		}
		parents->nextparent = newParent;
	}	
	// Increase the amount of parents the child has
	child->parentCount++;
}	

struct Three_AddressNode *add_ThreeAddress(int type, int LHS, int operand1, int operand2, int operator, char *expr, int exprHash) 
{
	// Reserve memory for the structure
	struct Three_AddressNode *newNode = malloc(sizeof(struct Three_AddressNode));

	// Setup the structure values
	newNode->type = type;
	if (LHS != -1) {
		newNode->LHS = LHS;
	}
	if (operand1 != -1) {
		newNode->operand1 = operand1;
	}
	if (operand2 != -1) {
		newNode->operand2 = operand2;
	}
	if (operator != -1) {
		newNode->operator = operator;
	}
	newNode->delFlag = 0;
	newNode->lineNumber = lineNumber;
	lineNumber++;

	// Set up the expression data
	if (type == THREE_ADDRESS_ASSIGN) {
		struct expression_tan *newExpr = new_expression_tan(expr, LHS, exprHash);
		newExpr->countRHS = exprRHSCount;
		copy_global_expr_rhs(&(newExpr->RHS));
		// Add the line number of the node to the expression for ease of reference later
		newExpr->countLines = 1;
		newExpr->Lines[0] = newNode->lineNumber;
		newNode->expr = newExpr;
	}
	// Add structure to the current basic block and return 
	add_to_cfg(newNode);

	// Add structute to the global list of nodes
	add_to_node_list(newNode);
	return newNode;
}

int evaluate_expression(struct expression_t *exp) 
{
	char * temp;
	if (exp->relop == -1) 
	{
		// No complex operation is going on - just return the simple expression 
		return evaluate_simple_expression(exp->se1);
	}
	else 
	{
		// Some value comparison is being done -return the left and right side simple expressions with the operation
		temp = next_term(); 
		int LHS = makekey(temp, MAX_VARIABLES);
		if (cfg_symtab_lookup(LHS) == NULL)
			cfg_symtab_add(SYM_TYPE_TEMP, LHS, temp);

		int operand1 = evaluate_simple_expression(exp->se1);
		add_key_to_global_rhs(operand1);
		int operand2 = evaluate_simple_expression(exp->se2);
		add_key_to_global_rhs(operand2);

		struct Three_AddressNode *newNode = add_ThreeAddress(THREE_ADDRESS_ASSIGN, LHS, operand1, operand2, exp->relop, exp->expr->type, exp->expr->val);
		return newNode->LHS;
	}
}

int evaluate_simple_expression(struct simple_expression_t *se) 
{
	char * temp;
	if (se->addop == -1) 
	{
		// No operation being performed - just return the term
		return evaluate_term(se->t);
	}	
	else 
	{
		// An operation is being performed - create a new temporary variable and set it to the operation at hand
		temp = next_term();
		int LHS = makekey(temp, MAX_VARIABLES);
		if (cfg_symtab_lookup(LHS) == NULL)
			cfg_symtab_add(SYM_TYPE_TEMP, LHS, temp);

		int operand1 = evaluate_simple_expression(se->next);
		add_key_to_global_rhs(operand1);
		int operand2 = evaluate_term(se->t);
		add_key_to_global_rhs(operand2);

		struct Three_AddressNode *newNode = add_ThreeAddress(THREE_ADDRESS_ASSIGN, LHS, operand1, operand2, se->addop, se->expr->type, se->expr->val);
		return newNode->LHS;		
	}
}

int evaluate_term(struct term_t *term) 
{
	char * temp;
	if (term->mulop == -1) 
	{
		// No operation being performed - just return the factor
		return evaluate_factor(term->f);
	}	
	else 
	{
		// An operation is being performed - create a new temporary variable and set it to the operation at hand
		temp = next_term();
		int LHS = makekey(temp, MAX_VARIABLES);
		if (cfg_symtab_lookup(LHS) == NULL)
			cfg_symtab_add(SYM_TYPE_TEMP, LHS, temp);

		int operand1 = evaluate_term(term->next);
		add_key_to_global_rhs(operand1);
		int operand2 = evaluate_factor(term->f);
		add_key_to_global_rhs(operand2);	

		struct Three_AddressNode *newNode = add_ThreeAddress(THREE_ADDRESS_ASSIGN, LHS, operand1, operand2, term->mulop, term->expr->type, term->expr->val);
		return newNode->LHS;
	}
}

int evaluate_factor(struct factor_t *f) 
{
	char * temp;
	if (f->type == FACTOR_T_SIGNFACTOR) 
	{
		// Treat each sign as an operation
		temp = next_term();
		int LHS = makekey(temp, MAX_VARIABLES);
		if (cfg_symtab_lookup(LHS) == NULL)
			cfg_symtab_add(SYM_TYPE_TEMP, LHS, temp);

		int operand2 = evaluate_factor(f->data.f.next);
		add_key_to_global_rhs(operand2);

		struct Three_AddressNode *newNode = add_ThreeAddress(THREE_ADDRESS_ASSIGN, LHS, -1, operand2, *(f->data.f.sign), f->expr->type, f->expr->val);
		return newNode->LHS;
	}
	else if (f->type == FACTOR_T_PRIMARY) 
	{
		// No operation being performed - just return the primary
		return evaluate_primary(f->data.p);
	}
	return -1;	
}

int evaluate_primary(struct primary_t *p) 
{
	if (p->type == PRIMARY_T_VARIABLE_ACCESS) 
	{
		// This is just a variable, so return the id of the variable
		return makekey(p->data.va->data.id, MAX_VARIABLES);
	}
	else if (p->type == PRIMARY_T_UNSIGNED_CONSTANT) 
	{
		// This is a number, so make it into a string and return the key of the string
		int LHS = getConstKey(p->data.un->ui);
		char *symName = inttostring(p->data.un->ui);
		if (cfg_symtab_lookup(LHS) == NULL)
			cfg_symtab_add(SYM_TYPE_CONST, LHS, symName);
		return LHS;
	}
	else if (p->type == PRIMARY_T_EXPRESSION) 
	{
		// Return the expression evaluation
		return evaluate_expression(p->data.e);
	} 
	else if (p->type == PRIMARY_T_PRIMARY) 
	{
		// A NOT operation is being performed
		int LHS = makekey(next_term(), MAX_VARIABLES);

		int operand2 = evaluate_primary(p->data.p.next);
		add_key_to_global_rhs(operand2);

		struct Three_AddressNode *newNode = add_ThreeAddress(THREE_ADDRESS_ASSIGN, LHS, -1, operand2, NOT, p->expr->type, p->expr->val);
		return newNode->LHS;
	}
	return -1;
}

int getConstKey(int constant)
{
	char *name = (char *)(malloc(32*sizeof(char)));
	strcpy(name, "const.");
	strcat(name, inttostring(constant));
	return makekey(name, MAX_VARIABLES);
}

char *next_term() 
{
	char *term = (char *)(malloc(32*sizeof(char)));
	strcpy(term, "temp.");
	strcat(term, inttostring(varCount));
	varCount++;
	return term;
}

void add_block() 
{
	if (currentBlock && currentBlock->exit == NULL) currentBlock->exit = currentNode;
	struct BB *newBlock = (struct BB *)(malloc(sizeof(struct BB)));
	blockCount++;
	newBlock->number = blockCount;
	newBlock->marked = 0;
	newBlock->parentCount = 0;
	newBlock->type = BB_TYPE_DEFAULT;
	currentBlock = newBlock;
	currentNode = NULL;
	add_to_bb_list(newBlock);
}

void print_block(struct BB * bb)
{
	bb->marked = 1; 
	printf("********START OF BLOCK *********%d\n", bb->number);

	/* print the current block */
	if (bb->entry == NULL) return;
	struct Three_AddressNode *curNode = bb->entry;

	while (curNode != NULL) 
	{
		print_three_address(curNode);

		curNode = curNode->next;
	}
	//	printf("***********************\n");
	//	printf("Points to block #: ");

	if (bb->exit->ptr1 != NULL) printf("\nchild1 is block %d\t", bb->exit->ptr1->number);
	if (bb->exit->ptr2 != NULL) printf("child2 is block %d\n", bb->exit->ptr2->number);
	printf("\n\n");
}

/* this function returns block only if it is dummy, else returns NULL */
struct BB * print_blocks(struct BB * bb, int force)
{
	struct Three_AddressNode *exitNodeOfBlock = bb->exit;
	struct BB * dummy1 = NULL;
	struct BB * dummy2 = NULL;

	/* if current block is pointing to dummy, then return */
	// If force is passed, then we have printed all of the parents of the dummy and can now print the dummy itself
	if ((bb->parentCount > 1 || bb->marked == 1) && force == 0) 
	{
		// Having a marked block in this case means we are visiting the while loop conditional block
		if (bb->marked == 1) return NULL;
		return bb;
	} 

	/* evaluate the current block */
	print_block(bb);

	/* recursive call to evaluate next block */
	if (exitNodeOfBlock->ptr1!=NULL)
		dummy1 = print_blocks(exitNodeOfBlock->ptr1, 0);

	if (exitNodeOfBlock->ptr2!=NULL)
		dummy2 = print_blocks(exitNodeOfBlock->ptr2, 0);

	/* check if they are pointing to dummies */
	if (dummy1 != NULL && dummy2 != NULL)
	{
		return print_blocks (dummy2, 1);
	}
	else if (dummy1 != NULL) return dummy1;
	else if (dummy2 != NULL) return dummy2;

	return NULL;      
}

void print_cfg (struct CFG *c) 
{
	if (c->entry == NULL) return;
	print_blocks(c->entry, 1);
}

void print_three_address(struct Three_AddressNode *currentNode)
{
	if (currentNode->type == THREE_ADDRESS_ASSIGN) 
	{
		if (currentNode->operand1 && currentNode->operand2) 
		{
			//printf("%d = %d %s %d;\n", currentNode->LHS, currentNode->operand1, convert_operator(currentNode->operator), currentNode->operand2);
			printf("%d %s = %s %s %s\n", currentNode->lineNumber, cfg_symtab_get_val(currentNode->LHS), cfg_symtab_get_val(currentNode->operand1), convert_operator(currentNode->operator), cfg_symtab_get_val(currentNode->operand2));
		} 
		else if (currentNode->operand1 && !(currentNode->operand2)) 
		{
			printf("%d %s = %s\n", currentNode->lineNumber, cfg_symtab_get_val(currentNode->LHS), cfg_symtab_get_val(currentNode->operand1));
			//printf("%d = %d;\n", currentNode->LHS, currentNode->operand1);
		} 
		else if (!(currentNode->operand1)&& currentNode->operand2) 
		{
			//printf("%d = %s %d;\n", currentNode->LHS, convert_operator(currentNode->operator), currentNode->operand2);
			printf("%d %s = %s %d\n", currentNode->lineNumber, cfg_symtab_get_val(currentNode->LHS), convert_operator(currentNode->operator), currentNode->operand2);
		}
	} 
	else if (currentNode->type == THREE_ADDRESS_IF) 
	{
		printf("%d if %s\n", currentNode->lineNumber, cfg_symtab_get_val(currentNode->LHS));
		//printf("if %d\n", currentNode->LHS);
	} 
	else if (currentNode->type == THREE_ADDRESS_WHILE) 
	{
		printf("%d while %s\n", currentNode->lineNumber, cfg_symtab_get_val(currentNode->LHS)); 
		//printf("while %d\n", currentNode->LHS); 
	} 
	else 
	{
		printf("Dummy\n");
	}
}

char *convert_operator(int operator) {
	if (operator == PLUS) return "+";
	else if (operator == MINUS) return "-";
	else if (operator == OR) return "||";
	else if (operator == EQUAL) return "=";
	else if (operator == NOTEQUAL) return "!=";
	else if (operator == LT) return "<";
	else if (operator == GT) return ">";
	else if (operator == LE) return "<=";
	else if (operator == GE) return ">=";
	else if (operator == NOT) return "NOT";
	else if (operator == STAR) return "*";
	else if (operator == SLASH) return "/";
	else if (operator == MOD) return "%";
	else if (operator == AND) return "AND";
	return NULL;
}

void remove_block_from_list(struct BB *bb)
{
	struct BBList *temp = bbList;
	if (temp == NULL) return;
	if (temp->bb->number == bb->number) {
		bbList = temp->next;
	} else {
		while (temp->next != NULL) {
			if (temp->next->bb->number == bb->number) {
				temp->next = temp->next->next;
				break;
			}
			temp = temp->next;
		}
	}
}

void removeDummies(struct CFG * c) {
	if (c->entry != NULL) removeDummiesBlock(c->entry);
}

void removeDummiesBlock(struct BB *block) {
	if (block == NULL || block->marked == 1) return;
	block->marked = 1;
	struct Three_AddressNode *entry = block->entry;
	if (entry->type == THREE_ADDRESS_DUMMY && entry->next == NULL) {
		// This is a dummy block, so remove it completely
		struct BB *nextBlock = entry->ptr1;
		// Cycle through the parents and set their next pointers to the next block
		struct parentBB *parent = block->parents;
		while (parent != NULL) {
			if (parent->parent->exit->ptr1 == block) {
				parent->parent->exit->ptr1 = nextBlock;
			} else if (parent->parent->exit->ptr2 == block) {
				parent->parent->exit->ptr2 = nextBlock;	
			}
			parent = parent->nextparent;
		}
		remove_block_from_list(block);
		free(block);
		removeDummiesBlock(nextBlock);
	} else {
		if (entry->type == THREE_ADDRESS_DUMMY) {
			// Remove the individual dummy node at the start of the block
			remove_from_node_list(block->entry);		
			block->entry = entry->next;
			free(entry);
		}
		// Continue removing dummies for the children
		struct Three_AddressNode *exit = block->exit;
		if (exit->ptr1 != NULL) removeDummiesBlock(exit->ptr1);
		if (exit->ptr2 != NULL) removeDummiesBlock(exit->ptr2);
	}	
}

int remove_unused_variables_block(struct BB *bb)
{
	if (bb == NULL) return;
	if (bb->entry == NULL) return;
	int result = 0;
	struct Three_AddressNode *tempNode = bb->entry;
	struct cfg_symtab_elem *lhs = cfg_symtab_lookup(tempNode->LHS);

	// Only remove for an assign statement that assigns a temporary variable, that does not appear in any rhs in the program
	if (tempNode->type == THREE_ADDRESS_ASSIGN && lhs->type == SYM_TYPE_TEMP && lhs->marked == 0) {
		result = 1;
		bb->entry = tempNode->next;
		cfg_symtab_remove_key(tempNode->LHS);
		remove_from_node_list(tempNode);
		free(tempNode);
		remove_unused_variables_block(bb);
	} else {
		while (tempNode->next != NULL) {
			struct Three_AddressNode *next = tempNode->next;
			lhs = cfg_symtab_lookup(next->LHS);
			if (next->type == THREE_ADDRESS_ASSIGN && lhs->type == SYM_TYPE_TEMP && lhs->marked == 0) {
				result = 1;
				tempNode->next = next->next;
				cfg_symtab_remove_key(next->LHS);
				remove_from_node_list(next);
				free(next);
			} else {
				tempNode = tempNode->next;
			}
		}
	}
	return result;
}

int remove_unused_variables()
{
	// Run through each block and each node in the block and remove any temporary variables that do not appear in a rhs throughout the program
	int result = 0;
	struct BBList *temp = bbList;
	while (temp != NULL) {
		int blockResult = remove_unused_variables_block(temp->bb);
		if (blockResult == 1) result = 1;
		temp = temp->next;
	}
	return result;
}

void cleanup_block(struct BB *bb)
{
	// Cycle through the three address nodes of the block and mark each right hand side
	struct Three_AddressNode *node = bb->entry;
	while (node != NULL) {
		if (node->operand1 != NULL) {
			struct cfg_symtab_elem *op1 = cfg_symtab_lookup(node->operand1);
			op1->marked = 1;
		}
		if (node->operand2 != NULL) {
			struct cfg_symtab_elem *op2 = cfg_symtab_lookup(node->operand2);
			op2->marked = 1;
		}
		if (node->type == THREE_ADDRESS_IF || node->type == THREE_ADDRESS_WHILE) {
			struct cfg_symtab_elem *lhs = cfg_symtab_lookup(node->LHS);
			lhs->marked = 1;
		}
		node = node->next;
	}
}

void cleanup_nodes()
{
	int changed = 1;
	while (changed == 1) {
		changed = 0;

		// Run through each block and evaluate used symbols
		struct BBList *temp = bbList;
		while (temp != NULL) {
			cleanup_block(temp->bb);
			temp = temp->next;
		}

		// See if any LHS need to be removed
		changed = remove_unused_variables();

		// Reset all variables to not be marked anymore
		cfg_symtab_reinit_mark();
	}	
}
